package ie.flax.flaxengine.client;
import ie.flax.flaxengine.client.Graphic.FCamera;
import ie.flax.flaxengine.client.Graphic.FImage;
import ie.flax.flaxengine.client.Graphic.Graphic;
import ie.flax.flaxengine.client.gamewidgets.SplashScreen;
import ie.flax.flaxengine.client.weave.Weave;
import java.util.ArrayList;
import java.util.List;
import com.google.gwt.canvas.client.Canvas;
import com.google.gwt.dom.client.Style.Position;
import com.google.gwt.event.dom.client.ClickEvent;
import com.google.gwt.event.dom.client.ClickHandler;
import com.google.gwt.event.dom.client.KeyDownEvent;
import com.google.gwt.event.dom.client.KeyDownHandler;
import com.google.gwt.event.dom.client.KeyPressEvent;
import com.google.gwt.event.dom.client.KeyPressHandler;
import com.google.gwt.event.dom.client.MouseDownEvent;
import com.google.gwt.event.dom.client.MouseDownHandler;
import com.google.gwt.event.dom.client.MouseMoveEvent;
import com.google.gwt.event.dom.client.MouseMoveHandler;
import com.google.gwt.event.dom.client.MouseUpEvent;
import com.google.gwt.event.dom.client.MouseUpHandler;
import com.google.gwt.event.logical.shared.ResizeEvent;
import com.google.gwt.event.logical.shared.ResizeHandler;
import com.google.gwt.user.client.Timer;
import com.google.gwt.user.client.Window;
import com.google.gwt.user.client.ui.RootPanel;
/**
* This class is the engine itself, all the other class are the components and
* will have objects or arrays of objects of these components inside this class.
* This is the main class which the user will be interfacing with. <br>
* <br>
* This class is abstract so it most be extended by the developers main game
* class
*
* @author Ciaran McCann
*/
public abstract class FlaxEngine {
public static Canvas drawingSpace;
private Canvas editorOverLay;
private final List<FMap> maps = new ArrayList<FMap>();
private int currentMap;
private boolean playing;
public static Settings settings;
private boolean engineStatus;
private int frameCount = 0;
private double oldMilliseconds = 0;
private final Weave editor;
private final SplashScreen splashScreen;
public static FCamera camera;
/**
* This is technically the game loop. It tells requestAnimationFrame to call
* it. Draw, update, etc should be in here.
*/
/*
private final TimerCallback gameTimer = new TimerCallback() {
@Override
public void fire() {
Graphic.getSingleton().requestAnimationFrame(this);
frameCount++;
double currentMilliseconds = getMilliseconds();
if (isEngineReady() && playing) {
maps.get(0).draw(camera, drawingSpace, currentMilliseconds);
}
if (currentMilliseconds < oldMilliseconds) {
editor.updateFps(frameCount);
frameCount = 0;
}
oldMilliseconds = currentMilliseconds;
}
};*/
private final Timer gameTimer = new Timer() {
public void run() {
frameCount++;
double currentMilliseconds = getMilliseconds();
if (isEngineReady() && playing) {
maps.get(0).draw(camera, drawingSpace, currentMilliseconds);
}
if (currentMilliseconds < oldMilliseconds) {
editor.updateFps(frameCount);
frameCount = 0;
}
oldMilliseconds = currentMilliseconds;
}
};
/**
* This is the engine constructor -
* @param mapPaths - URL to the map you wish to load with the engine
* @param insertId - the div which the engine/game will be inserted into
*/
public FlaxEngine(final String mapPaths, final String insertId) {
/**
* Construct the splash screen
*/
splashScreen = new SplashScreen();
RootPanel.get(insertId).add(splashScreen);
splashScreen.getElement().getStyle().setPosition(Position.ABSOLUTE);
FLog.init();
initEngine(insertId);
maps.add( new FMap(mapPaths));// Loads all the maps
/**
* Constructs the editor weave
*/
editor = new Weave(insertId, drawingSpace, editorOverLay, getCurrentMap());
}
/**
* Registers the main game canvas for Events
*/
private void bind() {
/**
* Just a quick key to start the rendering. As now by default the system
* doesn't render anything. As hosted mode gets killed by the rendering
*/
settings.getContainer().addDomHandler(new KeyPressHandler() {
@Override
public void onKeyPress(KeyPressEvent event) {
if(event.getCharCode() == 'z')
{
playing = !playing;
}
}
}, KeyPressEvent.getType());
// TODO Very strange bug which will not allow the attaching of these
// handlers
// inside the weave object. The drawingSpace reference is passed in, but
// crazy stuff happens like stackover flow. Leaving them here for the
// meantime as its no bigy
editorOverLay.addKeyDownHandler(new KeyDownHandler() {
@Override
public void onKeyDown(KeyDownEvent event) {
if (editor.isRunning()) {
editor.keyboardControls(event);
}
}
});
editorOverLay.addMouseMoveHandler(new MouseMoveHandler() {
@Override
public void onMouseMove(MouseMoveEvent event) {
if (editor.isRunning()) {
editor.onMouseMove(event);
}
event.stopPropagation();
}
});
editorOverLay.addMouseDownHandler(new MouseDownHandler() {
@Override
public void onMouseDown(MouseDownEvent event) {
event.preventDefault();
if (editor.isRunning()) {
editor.onMouseDown(event);
}
}
});
editorOverLay.addMouseUpHandler(new MouseUpHandler() {
@Override
public void onMouseUp(MouseUpEvent event) {
event.preventDefault();
if (editor.isRunning()) {
editor.onMouseUp(event);
}
}
});
editorOverLay.addClickHandler(new ClickHandler() {
@Override
public void onClick(ClickEvent event) {
event.preventDefault();
}
});
}
/**
* This constructs the settings, constructs the game drawingcanvas and also the editoroverlay canvas
* @param insertId
*/
protected void initEngine(String insertId) {
RootPanel.get().getAbsoluteLeft();
if (settings == null) {
settings = new Settings();
settings.setContainer(insertId);
}
int width = settings.getWidth();
int height = settings.getHeight();
//basically check if it's fullscreen. Better than a use-once variable in Settings.
if (width == Window.getClientWidth() && height == Window.getClientHeight() &&
settings.getContainer().getAbsoluteLeft() == 0 && settings.getContainer().getAbsoluteTop() == 0){
Window.enableScrolling(false);
}
drawingSpace = Canvas.createIfSupported();
drawingSpace.setWidth(width + "px");
drawingSpace.setHeight(height + "px");
drawingSpace.setCoordinateSpaceWidth(width);
drawingSpace.setCoordinateSpaceHeight(height);
editorOverLay = Canvas.createIfSupported();
editorOverLay.setWidth(width + "px");
editorOverLay.setHeight(height + "px");
editorOverLay.setCoordinateSpaceWidth(width);
editorOverLay.setCoordinateSpaceHeight(height);
if (drawingSpace == null) {
Window.alert("Sorry, your browser doesn't support the HTML5 Canvas element\n\n" + "Please upgrade to a new version of Chrome");
}
camera = new FCamera(new FVector(0, 0), width, height);
bind(); // sets the event handlers for canvas tag
settings.getContainer().add(drawingSpace,0,0);// insert into doc
settings.getContainer().add(editorOverLay,0,0);
/**
* This is the boot strap loader for images in the engine. When an image
* is loaded by an FImage object, it inserts the image into this div
* which is display none and this triggers a DOM load image
*/
settings.getContainer().add(FImage.getBootStrapDiv());
settings.getContainer().addHandler(new ResizeHandler() {
@Override
public void onResize(ResizeEvent event) {
settings.setWidth(event.getWidth());
settings.setHeight(event.getHeight());
}
}, ResizeEvent.getType());
}
/**
* Checks are all the engine componets are loaded and the data in them got
* from the server
*
* @return
*/
public boolean isEngineReady() {
/**
* Understand the below if() statment. The first condtion which is
* checked is the status of the engine which is, by default, false. As
* this method is called in the main loop we don't want all the checks
* been excuted so we store the status of true once we get it In the
* secound condtional statement this happens -> If first checks is the
* first map in the map list null and then the secound check it indexes
* that object and asks for a member Loaded. Which if true will then
* move onto checking if the graphics componet is ready This only checks
* if the first map is loaded, this is going on the idea that all other
* maps will load by the time they are needed
*/
if (engineStatus == true)
return true;
if ((maps.get(0) != null) && maps.get(0).getLoaded()
&& Graphic.getSingleton().isComponentReady() && FAudio.isComponentReady()) {
engineStatus = true;
settings.getContainer().remove(splashScreen);
/*
* Rudimentary check to see if the client's resolution is high enough to actually use weave
* only for debugging purposes (mobile etc) so remove this when there's a better way.
*/
if ((settings.getHeight() >= 768) && (settings.getWidth() >= 1024)){
editor.toggle();
}
}
return engineStatus;
}
/**
* Returns the current map which can the be used to modify the information
* in the map
*
* @return
*/
public FMap getCurrentMap() {
return maps.get(currentMap);
}
/**
* Gets a reference to the editor
* @return
*/
public Weave getEditor() {
return editor;
}
/**
* Gets a map with given ID
*
* @param mapID
* @return
*/
public FMap getMap(int mapID) {
return maps.get(mapID);
}
/**
* Searchs though the list of maps and return the one with supplied name
*
* @param mapName
* @return
*/
public FMap getMap(String mapName) {
for (FMap map : maps) {
if (map.getName() == mapName)
return map;
}
return null;
}
/**
* The run method starts the game loop
*/
public void run() {
playing = true;
//Graphic.getSingleton().requestAnimationFrame(gameTimer);
gameTimer.scheduleRepeating(1000/24);
}
/**
* The currentMap by setting the index
*
* @param setIndex
*/
public void setCurrentMap(int setIndex) {
currentMap = setIndex;
}
/**
* Sets the current map to the given name
*
* @param mapName
*/
public void setCurrentMap(String mapName) {
int mapIndex = 0;
for (FMap map : maps) {
if (map.getName() == mapName) {
currentMap = mapIndex;
break;
}
mapIndex++;
}
}
/**
* For FPS counter
*
* @return
*/
protected native double getMilliseconds() /*-{
return new Date().getMilliseconds();
}-*/;
}